docs(error-catalog): INVALID_FORMAT names what really checks a value (field type or a format rule) - #19878
Merged
objectstack-fleet[bot] merged 2 commits intoSep 24, 2026
Conversation
…s format The INVALID_FORMAT entry told authors to fix the failure by matching "the field's `format` constraint", but the write-time record validator never reads a field-level `format` key: its email / url / phone shape checks key on the field `type`, and a `format` validation rule (a different key) answers field-level `invalid_format`. No route emits the top-level INVALID_FORMAT at all, so the entry now says so and points at VALIDATION_FAILED + fields[].code, the same shape the INVALID_REFERENCE entry already uses. The VALIDATION_ERROR example's email entry now carries `invalid_email`, the code the Zod issue mapper actually produces for an email-format miss. Claude-Session: https://claude.ai/code/session_01VDtqoecgES7ScQYGbFVDRv Co-authored-by: Claude <noreply@anthropic.com>
Outside `autonumber` the key is a display hint and on `autonumber` it is the record-number pattern, so 'a display hint the server never checks' was only right for most types; what holds for every type is that no write-time check reads it. Claude-Session: https://claude.ai/code/session_01VDtqoecgES7ScQYGbFVDRv Co-authored-by: Claude <noreply@anthropic.com>
This was referenced Sep 23, 2026
Contributor
Author
Contract reviewServed-tier: ① Derived judgmentsAll measured on
② Semver levelNone (docs-only). One file under ③ Boundary flags
Implemented-by: VERDICT: PASS Landing still needs, separately from this verdict: every in-progress check green on this head, and the maintainer's Generated by Claude Code |
objectstack-fleet
Bot
deleted the
claude/issue-19848-error-catalog-invalid-format
branch
September 24, 2026 15:56
akarma-synetal
pushed a commit
to akarma-synetal/framework
that referenced
this pull request
Sep 28, 2026
…level codes that really arrive (objectstack-ai#20004) Fixes objectstack-ai#19879 Clause-②: no ## What `content/docs/api/error-catalog.mdx`, the `VALUE_TOO_LONG` and `VALUE_TOO_SHORT` entries only. Both entries gave a live cause and a fix, as if a client could branch on the code. No producer emits either code. Both entries now say so, following the shape the `INVALID_FORMAT` entry got in objectstack-ai#19878: the code is reserved, no route emits it today, and a length miss arrives as a field-level `max_length` / `min_length` entry. Each entry now says where that entry rides on each path: `400 VALIDATION_FAILED` with `fields[]` for record writes and Zod-parsed request bodies (top-level on `/data`, under `details` through the runtime dispatcher), and `400 SETTINGS_VALIDATION` with `details.fields[]` for a settings write. The fix line names both envelopes. Both entries stay because the enum still declares the codes. ## Evidence (measured on `origin/main` `e8f163fc`) - **No producer.** `git grep -nE 'VALUE_TOO_(LONG|SHORT)'` outside tests hits only the enum members `packages/spec/src/api/errors.zod.ts:58-59`, the baseline rows `scripts/error-status-unpinned-baseline.json:27-28`, this page, the generated `content/docs/references/**` pages, and ADR-0114, which records these members as a known wart. A grep for other spellings (`VALUE_TOO`, `TOO_LONG`, `TOO_SHORT`) finds only the unrelated `PASSWORD_TOO_SHORT` in a plugin-auth test. Positive control: `INVALID_FORMAT` hits `errors.zod.ts:57`. - **Record writes.** `packages/objectql/src/validation/record-validator.ts:695-699` sends `fail('max_length', { maxLength, actual })` and `fail('min_length', { minLength, actual })` for `BOUNDED_STRING_FIELD_TYPES`. `buildFieldError` puts that object on the wire as `fields[].constraint`, and the envelope's top-level code is `VALIDATION_FAILED` (`VALIDATION_FAILED_CODE`, `:195`). - **Zod-parsed request bodies.** `packages/spec/src/api/zod-issues-to-fields.ts:74-81` maps `too_small` / `too_big` to `min_length` / `max_length` when the value is not a number, bigint, date, array or set. Numbers and dates map to `min_value` / `max_value`, and arrays and sets to `min_items` / `max_items`. That is why the page says "a string". The REST routes that use this send `code: 'VALIDATION_FAILED'` (for example `packages/rest/src/rest-server.ts:8960-8963`). - **Settings writes (a different envelope).** `packages/services/service-settings/src/settings-service.ts:397-400` returns `max_length` / `min_length` with `constraint { minLength?, maxLength?, actual }` for a settings value outside its declared length window. `:2145` pushes it into the errors list, and `:2167` throws `SettingsValidationError` (`settings-service.types.ts:583-584`, `code = 'SETTINGS_VALIDATION'`). `packages/services/service-settings/src/settings-routes.ts:215-217` serves it as `sendError(res, 400, 'SETTINGS_VALIDATION', …, { details: { namespace, fields } })`, and `packages/types/src/response-envelope.ts` `sendError` writes that as `{ success: false, error: { code, message, details } }`. So a settings length miss is top-level `SETTINGS_VALIDATION` with the entry in `error.details.fields[]`, not `VALIDATION_FAILED`. - **Where `VALIDATION_FAILED` puts the list.** On the `/data` routes it is flat (`packages/rest/src/error-response.ts:1152-1160`, `mapDataError`: top-level `fields`). Through the runtime dispatcher it is nested (`packages/runtime/src/dispatcher-plugin.ts:645`, `validationFailureDetails`: `details.fields`). The page's own `VALIDATION_FAILED` callout already documents both, so the entries link to it rather than restating it. - The field-level spellings are the same on all three paths: `max_length` / `min_length`. The envelope differs: `VALIDATION_FAILED` for records and Zod bodies, `SETTINGS_VALIDATION` for settings. ## Not touched - The wire-count sentence at `:6`, the `CONCURRENT_LIMIT_EXCEEDED` entry and `scripts/error-status-unpinned-baseline.json` are not changed. Open PR objectstack-ai#19957 edits them. - `VALUE_OUT_OF_RANGE` and `MISSING_REQUIRED_FIELD` are not changed. Both have producers. - The page's frontmatter and headings are not changed. ## Changeset None. This is a docs-only change under `content/docs/`, and no published package's `files[]` changes, so it falls under `skip-changeset`. The PM seat applies the label. ## Gates Derived with `node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands` from the merge-base change set (1 path). All 41 derived commands ran on HEAD `37431df1` and exited 0. The derivation is unchanged from the first head, `ab8b19ea`. They include `pnpm check:doc-authoring`, `pnpm check:doc-anchors`, `pnpm check:nul-bytes`, `pnpm check:error-status-conformance`, `pnpm check:docs-spec-enumerations` and `pnpm --filter @objectstack/spec run check:docs`. The prerequisite closures (`lint` / `formula` / `client-react`, which pulls in `spec`) were built under the verify lock first. Reconciliation with `--ran` and the recorded exit codes: `41 derived, 41 run, 0 NOT-MEASURED, 0 UNRUN` (a derived zero). No package source changed, so no package tests or typecheck are owed. ## Rework (PM review) The first head said every length miss arrives in `VALIDATION_FAILED`, which is wrong for settings writes. The second commit, `37431df1`, names `SETTINGS_VALIDATION` + `details.fields[]` for that path in both the Cause and the Fix lines. The `#validation_failed` link resolves: `check:doc-anchors` passes, with 377 fragment links resolved. ## Acceptance notes - The page's intro counts "52 error codes reachable on the wire", but the page carries 53 code headings, and several of them are reserved codes with no emitter (`INVALID_FORMAT`, `INVALID_REFERENCE`, and now these two). Whether that count should include reserved codes is a question for the line PR objectstack-ai#19957 already edits. It is not changed here. --- _Generated by [Claude Code](https://claude.ai/code/session_01VDtqoecgES7ScQYGbFVDRv)_ --------- Co-authored-by: Claude <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #19848
Clause-②: no
What changed
content/docs/api/error-catalog.mdxonly.INVALID_FORMATentry. Its Fix line told authors to match "the field'sformatconstraint". No write-time check reads a field-levelformatkey, so following that advice changes nothing. The entry now says what actually decides:INVALID_FORMATtoday. The entry now says so and tells clients to branch onVALIDATION_FAILED+fields[].code. That is the same shape the page'sINVALID_REFERENCEentry already uses.type. The built-in email / url / phone checks key ontypeand answerinvalid_email/invalid_url/invalid_phone. Date and time parse failures answerinvalid_date/invalid_time.formatvalidation rule (a different key: itsregexor its namedformatemail|url|phone|json) answers field-levelinvalid_format. The link goes to/docs/data-modeling/validation#format-validation, the anchor PR docs(data-modeling): stop crediting fieldformatwith validation #19847 uses.invalid_formatis also emitted for a missed declaredpatternoutside record metadata: a settings value, or a request body a route parses with Zod.typeor theformatvalidation rule as the things to change, and says a field-levelformatkey runs no write-time check on any field type.VALIDATION_ERRORJSON example showed an email miss as"code": "invalid_format". The Zod mapper answersinvalid_emailfor that miss. See the Acceptance notes.The wording follows PR #19847 (still open at the time of writing; this PR depends on none of its files) and the spec's
formatdescribe: "keyed ontype", "a field-levelformatkey is not read", "aformatvalidation rule".Evidence (all at base
2bbb4623)format:def.format0 hits, same-file controldef.type7packages/objectql/src/validation/record-validator.tstype, emitinvalid_email/invalid_url/invalid_phonerecord-validator.ts:746-754invalid_date/invalid_timerecord-validator.ts:839,:858formatvalidation rule (regex or named format) emits field-levelinvalid_formatpackages/objectql/src/validation/rule-validator.ts:2776-2790(check),:2822(formatViolation)patternmiss emits field-levelinvalid_formatpackages/services/service-settings/src/settings-service.ts:2042invalid_email, url toinvalid_url, other format/regex toinvalid_formatpackages/spec/src/api/zod-issues-to-fields.ts:82-85INVALID_FORMAThas no producer:git grep INVALID_FORMAToutside tests anddisthits only the enum memberpackages/spec/src/api/errors.zod.ts:57, the ADR note and the unpinned baselinescripts/error-status-unpinned-baseline.json:15("documented with an HTTP status that NO producer ... declares"); ADR-0114 line 37 records the six field-shaped top-level members as a known wartpackages/spec/src/data/field.zod.ts:1090-1094(theformatdescribe: "the write-time record validator's built-in email, url and phone checks key ontype, never on this key")Verification (final head
40758ef8)node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commandsderived 41 commands. I ran all 41 on40758ef8: 41 exited 0.--ranprinted41 derived famil(ies) accounted for — 41 run, 0 NOT-MEASURED (a DERIVED zero — all 41 recorded an exit code and none of them is 3).d56a2a7f), four gates exited 3 (PREREQUISITE NOT MET):check:doc-formula-expressions,check:doc-security-posture,check:skill-examplesandcheck:docs-transcript-drift. The lint, formula and client packages had not been built yet. After those builds all four re-ran with exit 0.pnpm --filter @objectstack/spec exec vitest run --maxWorkers=2 src/api/error-catalog-docs.test.ts(the test that reads this page against the wire face):Test Files 1 passed (1) · Tests 5 passed (5)on40758ef8.dispatch-gateslists outside its derived total, and the path-scheduledBuild Docs/Test Corejobs.Changeset
Docs-only.
content/docs/**is not in any package'sfiles[], so this PR publishes nothing and falls underskip-changeset. Per the dispatch, this seat writes no labels.Acceptance notes
Bounded in-place fix (the
VALIDATION_ERRORexampleinvalid_format→invalid_email). All four exemption conditions hold:formatwhere the real check keys on the email type);zod-issues-to-fields.ts:83);It lies outside the claim's declared "(the
INVALID_FORMATentry)" sub-surface. The claim's file surface needs this entry added.content/docs/ui/forms.mdx:229(400 VALIDATION_FAILED· "object schema validators fail (required,format,length, …)"): read, not edited. It lists kinds of constraint in theWhencolumn and gives no fix, so it does not tell anyone to edit a fieldformatkey. It does not carry the same false meaning. Not listed as a defect.Sibling entries on the same page (a finding, not fixed here):
VALUE_TOO_LONGandVALUE_TOO_SHORTalso have no producer (git grepoutside tests/dist: 0 hits each; control'VALIDATION_FAILED': 70). Both appear inscripts/error-status-unpinned-baseline.json. The page still documents them as live causes. The record validator answers field-levelmax_length/min_lengthunderVALIDATION_FAILEDinstead. This is reported to the seat for filing and is out of scope for this card.Generated by Claude Code